<?php
namespace pttp;

use Workerman\Protocols\Http;
use Workerman\Protocols\HttpCache;
use Workerman\Worker;

class PttpServer extends \Workerman\Worker {

    /**
     * 默认mime类型
     *
     * @var string
     */
    protected static $defaultMimeType = 'text/html; charset=utf-8';

    /**
     * 服务器名到文件路径的转换
     *
     * @var array ['workerman.net'=>'/home', 'www.workerman.net'=>'home/www']
     */
    protected $serverRoot = array();

    /**
     * mime类型映射关系
     *
     * @var array
     */
    protected static $mimeTypeMap = array();


    /**
     * mzphp 配置文件
     *
     * @var array
     */
    protected $appConf = array();

    /**
     * 运行环境：debug / online
     *
     * @var string
     */
    protected $env = 'debug';

    /**
     * cache
     */
    protected $cache = NULL;

    /**
     * 添加站点域名与站点目录的对应关系，类似nginx的
     *
     * @param string $domain
     * @param string $root_path
     * @return void
     */
    public function addRoot($domain, $root_path) {
        $this->serverRoot[$domain] = $root_path;
        // load conf
        $this->appConf[$root_path] = include($root_path . 'conf/conf.' . $this->env . '.php');
        // remove temp file cache
        $old_files = glob($root_path . 'data/tmp/*.php');
        foreach ($old_files as $file) {
            @unlink($file);
        }

    }

    /**
     * 构造函数
     *
     * @param string $socket_name
     * @param array  $context_option
     */
    public function __construct($socket_name, $context_option = array()) {
        if ($context_option['env']) {
            $this->env = $context_option['env'];
            unset($context_option['env']);
        }
        list($scheme, $address) = explode(':', $socket_name, 2);
        parent::__construct('http:' . $address, $context_option);
        $this->name = __CLASS__;
        $this->cache = new \pttp\PttpCache(10000);
    }

    /**
     * 运行
     *
     * @see Workerman.Worker::run()
     */
    public function run() {
        $this->onWorkerStart = array($this, 'onWorkerStart');
        $this->onMessage = array($this, 'onMessage');
        parent::run();
    }

    /**
     * 进程启动的时候一些初始化工作
     *
     * @throws \Exception
     */
    public function onWorkerStart() {
        // 初始化HttpCache
        HttpCache::init();
        // 初始化mimeMap
        $this->initMimeTypeMap();
    }


    /**
     * 初始化mimeType
     *
     * @return void
     */
    public function initMimeTypeMap() {
        $mime_file = Http::getMimeTypesFile();
        if (!is_file($mime_file)) {
            $this->notice("$mime_file mime.type file not fond");
            return;
        }
        $items = file($mime_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if (!is_array($items)) {
            $this->log("get $mime_file mime.type content fail");
            return;
        }
        foreach ($items as $content) {
            if (preg_match("/\s*(\S+)\s+(\S.+)/", $content, $match)) {
                $mime_type = $match[1];
                $extension_var = $match[2];
                $extension_array = explode(' ', substr($extension_var, 0, -1));
                foreach ($extension_array as $extension) {
                    self::$mimeTypeMap[$extension] = $mime_type;
                }
            }
        }
    }

    /**
     * 运行 PHP 文件
     *
     * @param $root_dir
     * @param $file
     * @param $connection
     * @param $conf
     */
    public function _runPHP($root_dir, $file, $connection, $conf) {
        $cwd = getcwd();
        chdir($root_dir);
        ini_set('display_errors', 'off');
        // 缓冲输出
        ob_start();
        try {
            // $_SERVER变量
            $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
            $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
            $_SERVER['SCRIPT_FILENAME'] = $file;
            $_SERVER['ENV'] = $this->env;
            $_SERVER['CONF'] = $conf;
            // 加载核心
            $conf = &$_SERVER['CONF'];
            if (!class_exists('core')) {
                if (isset($conf['core_path'])) {
                    define('FRAMEWORK_EXTEND_PATH', $conf['core_path']);
                }
                // 临时目录
                define('FRAMEWORK_TMP_PATH', $conf['tmp_path']);
                // 日志目录
                define('FRAMEWORK_LOG_PATH', $conf['log_path']);
                // 包含核心框架文件，转交给框架进行处理。
                include_once FRAMEWORK_PATH . 'mzphp.php';
            }
            \core::run($conf);
        } catch (\Exception $e) {
            // 如果不是exit
            if ($e->getMessage() != 'jump_exit') {
                echo $e;
            }
        }
        $content = ob_get_clean();
        ini_set('display_errors', 'on');
        chdir($cwd);
        return $this->output_gzip($connection, $content, 0);
    }


    /**
     * 处理 HTTP 请求
     *
     * @param $connection
     * @param $data
     * @return mixed
     */
    public function onMessage($connection, $data) {
        // 请求的文件路径
        while (strpos($_SERVER['REQUEST_URI'], '//') !== false) {
            $_SERVER['REQUEST_URI'] = str_replace('//', '/', $_SERVER['REQUEST_URI']);
        }
        $url_info = parse_url($_SERVER['REQUEST_URI']);
        if (!$url_info) {
            Http::header('HTTP/1.1 400 Bad Request');
            return $connection->close('<h1>400 Bad Request</h1>');
        }

        $path = $url_info['path'];

        $path_info = pathinfo($path);
        $extension = isset($path_info['extension']) ? $path_info['extension'] : '';
        if (!$extension && substr($path, -1, 1) == '/') {
            $path = $path . 'index.php';
            $extension = 'php';
        }

        $root_dir = isset($this->serverRoot[$_SERVER['HTTP_HOST']]) ? $this->serverRoot[$_SERVER['HTTP_HOST']] : $this->serverRoot['*'];

        $file = str_replace('//', '/', "$root_dir/$path");

        //echo 'Request = ', $file, ", ext = ", $extension, "\r\n";

        // 对应的php文件不存在则直接使用根目录的index.php
        if ($extension === 'php' && !is_file($file)) {
            $file = "$root_dir/index.php";
            if (!is_file($file)) {
                $file = "$root_dir/index.html";
                $extension = 'html';
                if (!is_file($file)) {
                    $file = '';
                }
            }
        }
        $file = realpath($file);
        // 404
        if (empty($file) || !is_file($file)) {
            Http::header("HTTP/1.1 404 Not Found");
            Http::header("File: $root_dir/$path");
            return $connection->close('<html><head><title>404 页面不存在</title></head><body><center><h3>404 Not Found</h3></center></body></html>');
        }
        //
        switch ($extension) {
            case 'php':
                return $this->_runPHP($root_dir, $file, $connection, $this->appConf[$root_dir]);
            break;
            default:
                // 请求的是静态资源文件
                if (isset(self::$mimeTypeMap[$extension])) {
                    Http::header('Content-Type: ' . self::$mimeTypeMap[$extension]);
                } else {
                    Http::header('Content-Type: ' . self::$defaultMimeType);
                }
                // 获取文件信息
                $info = stat($file);

                $modified_time = $info ? date('D, d M Y H:i:s', $info['mtime']) . ' GMT' : '';

                // 如果有$_SERVER['HTTP_IF_MODIFIED_SINCE']
                if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) {
                    // 文件没有更改则直接304
                    if ($modified_time === $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
                        // 304
                        Http::header('HTTP/1.1 304 Not Modified');
                        // 发送给客户端
                        return $connection->close('');
                    }
                }
                if ($modified_time) {
                    Http::header("Last-Modified: $modified_time");
                }


                $allow_gzip = in_array($extension, array('txt', 'js', 'css'));

                $save_cache = 1;
                if ($allow_gzip) {
                    $cache_data = $this->cache->get($file);
                } else {
                    $cache_data = array();
                    $save_cache = 0;
                }

                if ($cache_data) {
                    $content = &$cache_data['content'];
                    $is_gzip = $cache_data['gzip'];
                    HTTP::header('Read-From: CACHE');
                } else {
                    $content = file_get_contents($file);
                    $content = $allow_gzip ? gzencode($content) : $content;
                    //save cache
                    if ($save_cache) {
                        $cache_data = array(
                            'dateline' => $info['mtime'],
                            'gzip' => $allow_gzip,
                            'content' => $content,
                            'size' => strlen($content),
                        );
                        $this->cache->put($file, $cache_data);
                    }
                    HTTP::header('Read-From: IO');
                    $is_gzip = $allow_gzip;
                }

                return $this->output_gzip($connection, $content, $is_gzip);

            break;
        }
    }

    /**
     * @param $connect
     * @param $content
     * @param $is_gzip
     */
    public function output_gzip($connection, $content, $is_gzip) {
        $request_gzip = strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false;

        if ($request_gzip) {
            HTTP::header('Content-Encoding: gzip');
            $content = $is_gzip ? $content : gzencode($content);
        }
        return $connection->close($content);
    }

}
